package instructions;

import values.Array;
import values.Integer;
import values.Value;
import expressions.BadExpressionException;
import expressions.Expression;
import base.AST;
import base.Environment;
import base.Identifier;
import base.Interpreter;

public class ArrayAssign implements Instruction {

	private Identifier identifier;
	private AST index;
	private AST expression;

	/**
	 *  Creates the instruction 
	 *  identifier[index] = expression
	 */
	public ArrayAssign(Identifier identifier, AST index, AST expression) {
		assert index instanceof Integer || index instanceof Expression;
		assert expression instanceof Value || expression instanceof Expression;

		this.identifier = identifier;
		this.index = index;
		this.expression = expression;
	}

	/**
	 * if the identifier is known, changes the value of identifier[index]
	 */
	public Environment instr(Environment env) throws BadInstructionException {

		assert env.contains(this.identifier);

		try {

			Integer intIndex = Interpreter.getInt(this.index, env);

			Value array = env.getValue(this.identifier);

			if (array instanceof Array) {

				Value newValue;
				Environment newEnv = env.clone();

				try {
					newValue = Interpreter.getBoolean(expression, env);
				} catch (BadExpressionException e1) {
					try {
						newValue = Interpreter.getInt(expression, env);
					} catch (BadExpressionException e2) {
						try {
							newValue = Interpreter.getArray(expression, env);
						} catch (BadExpressionException e3) {
							try {
								newValue = Interpreter.getString(expression,
										env);
							} catch (BadExpressionException e4) {
								throw new BadInstructionException(
										"Trying to assign a non-boolean, non-integer, non-array or non-string to the array "
												+ this.identifier.getName());
							}
						}
					}
				}

				((Array) array).update(intIndex.eval(), newValue);

				return newEnv;

			} else
				throw new BadInstructionException(this.identifier.getName()
						+ " is not an array.");

		} catch (BadExpressionException e) {
			throw new BadInstructionException(e,
					"During assignment of the array "
							+ this.identifier.getName());
		}

	}
}
